package com.hh.ui.widget;

import java.util.LinkedList;
import java.util.Queue;

import android.annotation.SuppressLint;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;
import com.hh.droid.R;
import com.hh.ui.UiUtility;

/**
 * An Horizontal List which use adapter to display data

 * @author WajdiHh + Other unknown developer (based on sample code found on Internet)
 *
 */
public class UiHorizontalListView extends AdapterView<ListAdapter> {

	private ListAdapter _mAdapter;
	private int _mLeftViewIndex = -1;
	private int _mRightViewIndex = 0;
	private int _mCurrentX;
	private int _mNextX;
	private int _mMaxX = Integer.MAX_VALUE;
	private int _mDisplayOffset = 0;
	private Scroller _mScroller;
	private GestureDetector _mGesture;
	private Queue<View> _mRemovedViewQueue = new LinkedList<View>();
	private OnItemSelectedListener _mOnItemSelected;
	private OnItemClickListener _mOnItemClicked;
	private int _mMarginDivider;
	private int _mDividerColor;

	public UiHorizontalListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initView();
	}
	
	/**
	 * Used to move to left side with 50 pixel
	 */
	public void moveToLeft() {
		_mNextX -= 50;
		requestLayout();
	}

	/**
	 * Used to move to left side with 50 pixel
	 */
	public void moveToRight() {
		_mNextX += 50;
		requestLayout();
	}

	public void setDividerColor(int pColor){
		_mDividerColor=pColor;
	}

	private void initView() {
		_mLeftViewIndex = -1;
		_mRightViewIndex = 0;
		_mDisplayOffset = 0;
		_mCurrentX = 0;
		_mNextX = 0;

		_mDividerColor=getResources().getColor(R.color.holo_gray_lev1);
		_mMarginDivider=10;
		_mMaxX = Integer.MAX_VALUE;
		_mScroller = new Scroller(getContext());
		_mGesture = new GestureDetector(getContext(), mOnGesture);

	}

	@Override
	public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
		_mOnItemSelected = listener;
	}

	@Override
	public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {
		_mOnItemClicked = listener;
	}

	public void setItemSpacing(int pSpacing){
		_mMarginDivider=pSpacing;
	}
	private DataSetObserver mDataObserver = new DataSetObserver() {

		@Override
		public void onChanged() {
			super.onChanged();
		}

		@Override
		public void onInvalidated() {
			super.onInvalidated();
		}

	};

	@Override
	public ListAdapter getAdapter() {
		return _mAdapter;
	}

	@Override
	public View getSelectedView() {
		return null;
	}

	@Override
	public void setAdapter(ListAdapter adapter) {
		if (_mAdapter != null) {
			_mAdapter.unregisterDataSetObserver(mDataObserver);
		}

		_mAdapter = adapter;
		_mAdapter.registerDataSetObserver(mDataObserver);
		initView();
		removeAllViewsInLayout();
		requestLayout();

	}

	@Override
	public void setSelection(int position) {
		// TODO: implement
	}
	private void addAndMeasureChild(final View child, int viewPos) {
		LayoutParams params = child.getLayoutParams();
		if (params == null) {
			params = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);			
		}

		child.setPadding(_mMarginDivider, 0, _mMarginDivider, 0);

		child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
				MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));

		child.setBackgroundDrawable(getLayer(child.getMeasuredWidth()));

		addViewInLayout(child, viewPos, params, true);
	}

	@SuppressLint("DrawAllocation")
	@Override
	protected synchronized void onLayout(boolean changed, int left, int top,int right, int bottom) {
		super.onLayout(changed, left, top, right, bottom);

		if (_mAdapter == null) {
			return;
		}

		if (_mScroller.computeScrollOffset()) {
			int scrollx = _mScroller.getCurrX();
			_mNextX = scrollx;
		}

		if (_mNextX < 0) {
			_mNextX = 0;
			_mScroller.forceFinished(true);
		}
		if (_mNextX > _mMaxX) {
			_mNextX = _mMaxX;
			_mScroller.forceFinished(true);
		}

		int dx = _mCurrentX - _mNextX;

		removeNonVisibleItems(dx);
		fillList(dx);
		positionItems(dx);

		_mCurrentX = _mNextX;

		if (!_mScroller.isFinished()) {
			post(new Runnable() {
				@Override
				public void run() {
					requestLayout();
				}
			});

		}
	}

	private void fillList(final int dx) {

		int edge = 0;
		View child = getChildAt(getChildCount() - 1);
		if (child != null) {
			edge = child.getRight();
		}
		fillListRight(edge, dx);

		edge = 0;
		child = getChildAt(0);
		if (child != null) {
			edge = child.getLeft();
		}
		fillListLeft(edge, dx);

	}

	private void fillListRight(int rightEdge, final int dx) {
		while (rightEdge + dx < getWidth()&& _mRightViewIndex < _mAdapter.getCount()) {

			View child = _mAdapter.getView(_mRightViewIndex,_mRemovedViewQueue.poll(), this);

			addAndMeasureChild(child, -1);
			rightEdge += child.getMeasuredWidth();

			if (_mRightViewIndex == _mAdapter.getCount() - 1) {
				_mMaxX = _mCurrentX + rightEdge - getWidth();
			}
			if (_mMaxX < 0) 
				_mMaxX = 0;
			_mRightViewIndex++;
		}

	}

	private void fillListLeft(int leftEdge, final int dx) {
		while (leftEdge + dx > 0 && _mLeftViewIndex >= 0) {
			View child = _mAdapter.getView(_mLeftViewIndex,_mRemovedViewQueue.poll(), this);
			addAndMeasureChild(child, 0);
			leftEdge -= child.getMeasuredWidth();
			_mLeftViewIndex--;
			_mDisplayOffset -= child.getMeasuredWidth();
		}
	}

	private void removeNonVisibleItems(final int dx) {
		View child = getChildAt(0);
		while (child != null && child.getRight() + dx <= 0) {
			_mDisplayOffset += child.getMeasuredWidth();
			_mRemovedViewQueue.offer(child);
			removeViewInLayout(child);
			_mLeftViewIndex++;
			child = getChildAt(0);

		}

		child = getChildAt(getChildCount() - 1);
		while (child != null && child.getLeft() + dx >= getWidth()) {
			_mRemovedViewQueue.offer(child);
			removeViewInLayout(child);
			_mRightViewIndex--;
			child = getChildAt(getChildCount() - 1);
		}
	}

	private void positionItems(final int dx) {

		if (getChildCount() > 0) {
			_mDisplayOffset += dx;
			int left = _mDisplayOffset;
			for (int i = 0; i < getChildCount(); i++) {
				View child = getChildAt(i);
				int childWidth = child.getMeasuredWidth();
				child.layout(left, 0, left + childWidth,child.getMeasuredHeight());
				left += childWidth;
			}
		}
	}

	public synchronized void scrollTo(int x) {
		_mScroller.startScroll(_mNextX, 0, x - _mNextX, 0);
		requestLayout();
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {

		Rect viewRect = new Rect();
		for (int i = 0; i < getChildCount(); i++) {
			View child = getChildAt(i);
			int left = child.getLeft();
			int right = child.getRight();
			int top = child.getTop();
			int bottom = child.getBottom();
			viewRect.set(left, top, right, bottom);

			if (viewRect.contains((int) event.getX(), (int) event.getY())) {

				switch (event.getAction()) {
				case MotionEvent.ACTION_DOWN:
					child.setBackgroundColor(getContext().getResources().getColor(R.color.holo_blue_lev0));
					break;

				case MotionEvent.ACTION_UP:
					child.setBackgroundDrawable(getLayer(child.getMeasuredWidth()));
					break;
				}

				break;
			}
		}

		boolean handled = _mGesture.onTouchEvent(event);
		return handled;
	}

	protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {
		synchronized (UiHorizontalListView.this) {
			_mScroller.fling(_mNextX, 0, (int) -velocityX, 0, 0, _mMaxX, 0, 0);
		}
		requestLayout();

		return true;
	}

	protected boolean onDown(MotionEvent e) {
		_mScroller.forceFinished(true);
		return true;
	}

	private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {

		@Override
		public boolean onDown(MotionEvent e) {
			return UiHorizontalListView.this.onDown(e);
		}

		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {
			return UiHorizontalListView.this.onFling(e1, e2, velocityX, velocityY);
		}

		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {

			synchronized (UiHorizontalListView.this) {
				_mNextX += (int) distanceX;

				Rect viewRect = new Rect();
				for (int i = 0; i < getChildCount(); i++) {
					View child = getChildAt(i);
					int left = child.getLeft();
					int right = child.getRight();
					int top = child.getTop();
					int bottom = child.getBottom();
					viewRect.set(left, top, right, bottom);

					if (viewRect.contains((int) e1.getX(), (int) e1.getY())) {
						child.setBackgroundDrawable(getLayer(child.getMeasuredWidth()));
						break;
					}
				}
			}
			requestLayout();

			return true;
		}


		@Override
		public boolean onSingleTapConfirmed(MotionEvent event) {
			Rect viewRect = new Rect();
			for (int i = 0; i < getChildCount(); i++) {
				View child = getChildAt(i);
				int left = child.getLeft();
				int right = child.getRight();
				int top = child.getTop();
				int bottom = child.getBottom();
				viewRect.set(left, top, right, bottom);

				if (viewRect.contains((int) event.getX(), (int) event.getY())) {

					if (_mOnItemClicked != null) {
						_mOnItemClicked.onItemClick(UiHorizontalListView.this,child, _mLeftViewIndex + 1 + i, 0);
					}
					if (_mOnItemSelected != null) {
						_mOnItemSelected.onItemSelected(UiHorizontalListView.this,child, _mLeftViewIndex + 1 + i, 0);
					}
					break;
				}
			}
			return true;
		}		
	};

	private LayerDrawable getLayer(int widht){

		ShapeDrawable s1=UiUtility.createShapRec(0, 0, 0, 0,_mDividerColor);
		ShapeDrawable s2=UiUtility.createShapRec(0, 0, 0, 0,getResources().getColor(android.R.color.transparent));

		Drawable[] layers={s1,s2};
		LayerDrawable lList=new LayerDrawable(layers);
		lList.setLayerInset(0, widht-1, 15, 0, 15);
		lList.setLayerInset(1, 0, 0, 1, 0);

		return lList;
	}

}